package com.datang.litejvm.cmd;


import com.datang.litejvm.constant.ConstantPool;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author: 顶风少年
 * @Description: 解析字节码指令
 * @date: 22:05 2022/6/15
 **/
public class CommandParser {


    public static List<ByteCodeCommand> parse(List<String> codes, ConstantPool pool) {
        if (codes.size() == 0) {
            throw new RuntimeException("the orignal code is not correct");
        }
        List<ByteCodeCommand> cmds = new ArrayList<>();
        Iterator<String> iter = codes.iterator();
        while (iter.hasNext()) {
            String code = iter.next();
            if (ByteCodeCommand.new_object.equals(code)) {
                NewObjectCmd cmd = new NewObjectCmd(pool, code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.invokespecial.equals(code)) {
                InvokeSpecialCmd cmd = new InvokeSpecialCmd(pool, code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.invokevirtual.equals(code)) {
                InvokeVirtualCmd cmd = new InvokeVirtualCmd(pool, code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.getfield.equals(code)) {
                GetFieldCmd cmd = new GetFieldCmd(pool, code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.getstatic.equals(code)) {
                GetStaticFieldCmd cmd = new GetStaticFieldCmd(pool, code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.putfield.equals(code)) {
                PutFieldCmd cmd = new PutFieldCmd(pool, code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.ldc.equals(code)) {
                LdcCmd cmd = new LdcCmd(pool, code);
                cmd.setOperand(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.bipush.equals(code)) {
                BiPushCmd cmd = new BiPushCmd(pool, code);
                cmd.setOperand(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.if_icmpne.equals(code)||ByteCodeCommand.if_icmple.equals(code)||ByteCodeCommand.if_icmpge.equals(code)||ByteCodeCommand.if_icmpgt.equals(code)) {
                ComparisonCmd cmd = new ComparisonCmd(pool,code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if (ByteCodeCommand.goto_no_condition.equals(code)) {
                GotoCmd cmd = new GotoCmd(pool,code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            } else if(ByteCodeCommand.iinc.equals(code)){
                IncrementCmd cmd = new IncrementCmd(pool,code);
                cmd.setOprand1(Integer.valueOf(iter.next(), 16).intValue());
                cmd.setOprand2(Integer.valueOf(iter.next(), 16).intValue());
                cmds.add(cmd);
            }else if (ByteCodeCommand.dup.equals(code)
                    || ByteCodeCommand.aload_0.equals(code) || ByteCodeCommand.aload_1.equals(code) || ByteCodeCommand.aload_2.equals(code) || ByteCodeCommand.aload_3.equals(code)
                    || ByteCodeCommand.iload_0.equals(code) || ByteCodeCommand.iload_1.equals(code) || ByteCodeCommand.iload_2.equals(code) || ByteCodeCommand.iload_3.equals(code)
                    || ByteCodeCommand.fload_3.equals(code)
                    || ByteCodeCommand.voidreturn.equals(code)
                    || ByteCodeCommand.astore_0.equals(code) || ByteCodeCommand.astore_1.equals(code) || ByteCodeCommand.astore_2.equals(code) || ByteCodeCommand.astore_3.equals(code)
                    || ByteCodeCommand.iadd.equals(code)
                    || ByteCodeCommand.istore_0.equals(code) || ByteCodeCommand.istore_1.equals(code) || ByteCodeCommand.istore_2.equals(code) || ByteCodeCommand.istore_3.equals(code)
                    || ByteCodeCommand.iconst_0.equals(code)  || ByteCodeCommand.iconst_1.equals(code) || ByteCodeCommand.iconst_2.equals(code)|| ByteCodeCommand.iconst_3.equals(code)  ) {
                NoOperandCmd cmd = new NoOperandCmd(pool, code);
                cmds.add(cmd);
            } else {
                throw new RuntimeException("Sorry, the java instruction " + code + " has not been implemented");
            }
        }
        calculateOffset(cmds);
        return cmds;
    }

    private static void calculateOffset(List<ByteCodeCommand> cmds) {
        int offset = 0;
        for (ByteCodeCommand cmd : cmds) {
            cmd.setOffset(offset);
            offset += cmd.getLength();
        }
    }
}
